package game.controller;

import game.entity.*;
import game.util.Direction;
import game.util.FilesReader;
import game.util.GameConstant;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.fxml.FXML;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.image.Image;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.paint.Color;
import javafx.util.Duration;

import java.util.ArrayList;
import java.util.List;

public class GameController {

    @FXML
    private Canvas canvas;

    private GraphicsContext gc;

    private Empty[][] empties;

    private List<Brick> bricks;

    private List<Bomb> bombList;

    private List<Wall> walls;

    private Exit exit;

    private Player player;

    private List<Enemy> enemies;

    private Timeline timeline;

    private int count;

    private int levelNumber;

    private int second;

    private Image clockImage = new Image("file:src/game/image/clock.png");

    @FXML
    public void initialize() {
        bombList = new ArrayList<>();
        gc = canvas.getGraphicsContext2D();
        canvas.requestFocus();
        canvas.setFocusTraversable(true);
        canvas.setOnKeyPressed(this::addKeyEventListener);
        loadLevel("src/game/level/level1.txt");
        show();
        run();
    }

    private void addKeyEventListener(KeyEvent event) {
        KeyCode code = event.getCode();
        switch (code) {
            case UP:
                player.changeDirection(Direction.UP);
                if (canMove(player.getX(), player.getY() - 32 + 16)) {
                    player.setY(player.getY() - 32);
                }
                break;
            case DOWN:
                player.changeDirection(Direction.DOWN);
                if (canMove(player.getX(), player.getY() + 32 + 16)) {
                    player.setY(player.getY() + 32);
                }
                break;
            case LEFT:
                player.changeDirection(Direction.LEFT);
                if (canMove(player.getX() - 32, player.getY() + 16)) {
                    player.setX(player.getX() - 32);
                }
                break;
            case RIGHT:
                player.changeDirection(Direction.RIGHT);
                if (canMove(player.getX() + 32, player.getY() + 16)) {
                    player.setX(player.getX() + 32);
                }
                break;
            case SPACE:
                bombList.add(new Bomb(player.getX(), player.getY() + 16, ' '));
                break;
            default:
                break;
        }
    }

    private void run() {
        timeline = new Timeline(new KeyFrame(Duration.millis(50), t -> show()));
        timeline.setCycleCount(-1);
        timeline.play();
    }

    private void loadLevel(String levelFilePath) {
        empties = new Empty[13][15];
        bricks = new ArrayList<>();
        walls = new ArrayList<>();
        enemies = new ArrayList<>();

        List<String> strings = FilesReader.readLevel(levelFilePath);
        for (int i = 0; i < strings.size(); i++) {
            String line = strings.get(i);
            for (int j = 0; j < line.toCharArray().length; j++) {
                char c = line.charAt(j);
                empties[i][j] = new Empty(j * GameConstant.PIXEL, 64 + i * GameConstant.PIXEL, c);
                if (c == 'B') {
                    bricks.add(new Brick(j * GameConstant.PIXEL, 64 + i * GameConstant.PIXEL, c));
                }

                if (c == 'W') {
                    walls.add(new Wall(j * GameConstant.PIXEL, 64 + i * GameConstant.PIXEL, c));
                }

                if (c == 'G') {
                    exit = new Exit(j * GameConstant.PIXEL, 64 + i * GameConstant.PIXEL, c);
                }

                if (c == 'R') {
                    enemies.add(new Enemy(j * GameConstant.PIXEL, 64 + i * GameConstant.PIXEL + GameConstant.PIXEL - GameConstant.PERSON_HEIGHT, c));
                }

                if (c == 'Y') {
                    enemies.add(new Enemy(j * GameConstant.PIXEL, 64 + i * GameConstant.PIXEL + GameConstant.PIXEL - GameConstant.PERSON_HEIGHT, c));
                }

                if (c == 'P') {
                    player = new Player(j * GameConstant.PIXEL, 64 + i * GameConstant.PIXEL + GameConstant.PIXEL - GameConstant.PERSON_HEIGHT, c);
                }
            }
        }
        levelNumber++;
    }

    private void doPlayerDead() {
        player.reduceLiveNumber();
        player.resetPosition();
        player.changeDirection(Direction.DOWN);
        count = 0;
    }

    public void show() {
        if (player.getLiveNumber() <= 0) {
            gc.clearRect(0, 0, GameConstant.WIDTH, GameConstant.HEIGHT);
            gc.setFill(Color.rgb(237, 109, 0));
            gc.fillRect(0, 0, GameConstant.WIDTH, GameConstant.HEIGHT);
            gc.setStroke(Color.WHITE);
            gc.strokeText("you failed", 240, 240);
            return;
        }


        if (dead()) {
            doPlayerDead();
            return;
        }

        if (collideWithGate()) {
            if (levelNumber == 2) {
                gc.clearRect(0, 0, GameConstant.WIDTH, GameConstant.HEIGHT);
                gc.setFill(Color.rgb(237, 109, 0));
                gc.fillRect(0, 0, GameConstant.WIDTH, GameConstant.HEIGHT);
                gc.setStroke(Color.WHITE);
                gc.strokeText("you win", 240, 240);
                gc.strokeText("you spend " + (second / 1000) + " s", 200, 300);
                return;
            } else {
                loadLevel("src/game/level/level2.txt");
                return;
            }
        }
        count++;
        second = second + 50;
        gc.clearRect(0, 0, GameConstant.WIDTH, GameConstant.HEIGHT);
        gc.setFill(Color.rgb(237, 109, 0));
        gc.fillRect(0, 0, GameConstant.WIDTH, GameConstant.HEIGHT);
        // 绘制玩家头像
        gc.drawImage(player.getHeadIcon(), 3 * GameConstant.PIXEL, 0.5 * GameConstant.PIXEL);
        gc.setFill(Color.BLACK);
        gc.fillText("   " + player.getLiveNumber() + "", 4 * GameConstant.PIXEL, 1.2 * GameConstant.PIXEL);
        // 绘制时间
        gc.drawImage(clockImage, 10 * GameConstant.PIXEL, 0.5 * GameConstant.PIXEL);
        gc.fillText("   " + (second / 1000) + "", 11 * GameConstant.PIXEL, 1.2 * GameConstant.PIXEL);
        for (Empty[] empty : empties) {
            for (Empty e : empty) {
                e.draw(gc, count);
            }
        }

        for (Wall wall : walls) {
            wall.draw(gc, count);
        }

        for (Brick brick : bricks) {
            brick.draw(gc, count);
        }

        exit.draw(gc, count);
        for (Bomb bomb : bombList) {
            bomb.draw(gc, count);
        }
        player.draw(gc, count);

        for (Enemy enemy : enemies) {
            if (count % 10 == 0) {
                Direction direction = enemy.getDirection();
                boolean canMove;
                if (direction == Direction.UP) {
                    canMove = canMove(enemy.getX(), enemy.getY() - 32 + 16);
                } else if (direction == Direction.DOWN) {
                    canMove = canMove(enemy.getX(), enemy.getY() + 32 + 16);
                } else if (direction == Direction.LEFT) {
                    canMove = canMove(enemy.getX() - 32, enemy.getY() + 16);
                } else {
                    canMove = canMove(enemy.getX() + 32, enemy.getY() + 16);
                }
                if (canMove) {
                    enemy.move();
                } else {
                    enemy.changeDirection();
                }
            }
            enemy.draw(gc, count);
        }

        for (Bomb bomb : bombList) {
            if (bomb.isDestroy()) {
                List<String> positions = new ArrayList<>();
                positions.add(bomb.getX() + "," + bomb.getY());
                positions.add(bomb.getX() + "," + (bomb.getY() - 32));
                positions.add(bomb.getX() + "," + (bomb.getY() - 64));
                positions.add(bomb.getX() + "," + (bomb.getY() + 32));
                positions.add(bomb.getX() + "," + (bomb.getY() + 64));
                positions.add((bomb.getX() - 32) + "," + bomb.getY());
                positions.add((bomb.getX() - 64) + "," + bomb.getY());
                positions.add((bomb.getX() + 32) + "," + bomb.getY());
                positions.add((bomb.getX() + 64) + "," + bomb.getY());
                for (String position : positions) {
                    String[] split = position.split(",");
                    breakOthers(Integer.parseInt(split[0]), Integer.parseInt(split[1]));
                }
            }
        }

        bombList.removeIf(Bomb::isDestroy);
    }

    public void breakOthers(int x, int y) {
        if (player.getX() == x && player.getY() + 16 == y) {
            doPlayerDead();
            return;
        }

        for (Enemy e : enemies) {
            if (e.getX() == x && e.getY() + 16 == y) {
                e.setDead();
            }
        }

        for (Brick b : bricks) {
            if (b.getX() == x && b.getY() == y) {
                b.setDead();
            }
        }
    }

    private boolean collideWithGate() {
        if (player.getX() == exit.getX() && player.getY() + 16 == exit.getY()) {
            return true;
        }
        return false;
    }

    public boolean canMove(int x, int y) {
        for (Wall wall : walls) {
            if (wall.getX() == x && y == wall.getY()) {
                return false;
            }
        }

        for (Brick brick : bricks) {
            if (brick.getX() == x && y == brick.getY() && !brick.isDead()) {
                return false;
            }
        }
        return true;
    }

    public boolean dead() {
        for (Enemy enemy : enemies) {
            if (enemy.getX() == player.getX() && enemy.getY() == player.getY() && !enemy.isDead()) {
                return true;
            }
        }
        return false;
    }
}
